﻿using PetkaWebDav.Server.Framework.BaseClasses;
using PetkaWebDav.Server.Framework.Collections;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml;

namespace PetkaWebDav.Server.Framework
{
    /// Static class for internal functions
    /// </summary>
    internal sealed class InternalFunctions
    {
        private static object FileCriticalSection = new object();

        private static CultureInfo m_EnglishCulture = new CultureInfo("en-US");


        public static string GetMimeType(string extension)
        {
            return System.Web.MimeMapping.GetMimeMapping(extension);
        
        }

        /// <summary>
        /// Internal function for retrieving the opaque lock token
        /// </summary>
        /// <param name="inputString"></param>
        /// <returns></returns>
        internal static string ParseOpaqueLockToken(string inputString)
        {
            string _opaqueLockToken = "";

            if (inputString != null)
            {
                string _prefixTag = "<opaquelocktoken:";
                int _prefixIndex = inputString.IndexOf(_prefixTag);
                if (_prefixIndex != -1)
                {
                    int _endIndex = inputString.IndexOf('>', _prefixIndex);
                    if (_endIndex > _prefixIndex)
                        _opaqueLockToken = inputString.Substring(_prefixIndex + _prefixTag.Length, _endIndex - (_prefixIndex + _prefixTag.Length));
                }
            }

            return _opaqueLockToken;
        }

        /// <summary>
        /// Internal function to process response
        /// </summary>
        /// <param name="errorResources"></param>
        /// <returns></returns>
        internal static string ProcessErrorRequest(DavProcessErrorCollection errorResources)
        {
            return ProcessErrorRequest(errorResources, null);
        }

        /// <summary>
        /// Internal function to retrieve a valid enumerator's value
        /// </summary>
        /// <param name="statusCode"></param>
        /// <returns></returns>
        /// <exception cref="WebDavException">Throw exception if the statusCode value is not a valid Int32</exception>
        internal static int GetEnumValue(Enum statusCode)
        {
            int _enumValue = 0;
            if (ValidateEnumType(statusCode))
                _enumValue = (int)System.Enum.Parse(statusCode.GetType(), statusCode.ToString(), true);

            return _enumValue;
        }

        /// <summary>
        /// Retrieve the HTTP response status
        /// </summary>
        /// <param name="statusCode"></param>
        /// <returns></returns>
        internal static string GetEnumHttpResponse(Enum statusCode)
        {
            string _httpResponse = "";

            switch (InternalFunctions.GetEnumValue(statusCode))
            {
                case 200:
                    _httpResponse = "HTTP/1.1 200 OK";
                    break;

                case 404:
                    _httpResponse = "HTTP/1.1 404 Not Found";
                    break;

                case 423:
                    _httpResponse = "HTTP/1.1 423 Locked";
                    break;

                case 424:
                    _httpResponse = "HTTP/1.1 424 Failed Dependency";
                    break;

                case 507:
                    _httpResponse = "HTTP/1.1 507 Insufficient Storage";
                    break;

                default:
                    throw new WebDavException(InternalFunctions.GetResourceString("InvalidStatusCode"));
            }

            return _httpResponse;
        }

        /// <summary>
        /// Internal function to process response
        /// </summary>
        /// <param name="errorResources"></param>
        /// <param name="appendResponseNode"></param>
        /// <returns></returns>
        internal static string ProcessErrorRequest(DavProcessErrorCollection errorResources, XmlNode appendResponseNode)
        {
            string _errorRequest = "";

            //Build the response 
            using (Stream _responseStream = new MemoryStream())
            {
                XmlTextWriter _xmlWriter = new XmlTextWriter(_responseStream, new UTF8Encoding(false));

                _xmlWriter.Formatting = Formatting.Indented;
                _xmlWriter.IndentChar = '\t';
                _xmlWriter.Indentation = 1;
                _xmlWriter.WriteStartDocument();

                //Set the Multistatus
                _xmlWriter.WriteStartElement("D", "multistatus", "DAV:");

                //Append the errors
                foreach (Enum _errorCode in errorResources.AllResourceErrors)
                {
                    foreach (DavResourceBase _resource in errorResources[_errorCode])
                    {
                        //Open the response element
                        _xmlWriter.WriteStartElement("response", "DAV:");
                        _xmlWriter.WriteElementString("href", "DAV:", _resource.ResourcePath);
                        _xmlWriter.WriteElementString("status", "DAV:", GetEnumHttpResponse(_errorCode));
                        //Close the response element section
                        _xmlWriter.WriteEndElement();
                    }
                }

                if (appendResponseNode != null)
                    appendResponseNode.WriteTo(_xmlWriter);

                _xmlWriter.WriteEndElement();
                _xmlWriter.WriteEndDocument();
                _xmlWriter.Flush();

                using (StreamReader _streamReader = new StreamReader(_responseStream, Encoding.UTF8))
                {
                    //Go to the begining of the stream
                    _streamReader.BaseStream.Position = 0;
                    _errorRequest = _streamReader.ReadToEnd();
                }
                _xmlWriter.Close();
            }
            return _errorRequest;
        }

        /// <summary>
        /// Internal function to validate the enumerator is of type Int32
        /// </summary>
        /// <param name="enumToValidate"></param>
        /// <exception cref="WebDavException">Throw exception if the enumToValidate value is not a valid Int32</exception>
        internal static bool ValidateEnumType(Enum enumToValidate)
        {
            if (enumToValidate.GetTypeCode() != TypeCode.Int32)
                throw new WebDavException(InternalFunctions.GetResourceString("InvalidEnumIntType"));

            return true;
        }

        /// <summary>
        /// Iternal function to write debug file
        /// </summary>
        /// <param name="message"></param>
        internal static void WriteDebugLog(string message)
        {
            Trace.WriteLine(message);
            if (!string.IsNullOrEmpty(WebDavProcessor.DebugFilePath))
                InternalFunctions.AppendFile(WebDavProcessor.DebugFilePath, message);
        }

        /// <summary>
        /// Internal function to append a file
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="text"></param>
        private static void AppendFile(string filePath, string text)
        {
            lock (FileCriticalSection)
            {
                using (System.IO.StreamWriter _file = File.AppendText(filePath))
                {
                    _file.WriteLine(DateTime.Now.ToString("MM/dd/yy HH:mm:ss"));
                    _file.WriteLine(text);
                    _file.WriteLine();
                    _file.Close();
                }
            }
        }

        /// <summary>
        /// Internal function for obtaining a URIPath's relative path
        /// </summary>
        /// <param name="httpApplication"></param>
        /// <param name="URIPath"></param>
        /// <returns></returns>
        internal static string GetFileName(HttpApplication httpApplication, string URIPath)
        {

            string _retValue = string.Empty;

            if (httpApplication == null)
                throw new ArgumentNullException("HttpApplication", InternalFunctions.GetResourceString("ArgumentNullException", "HttpApplication"));

            string _nonPathPart = GetNonPathPart(httpApplication);

            try
            {
                _retValue = httpApplication.Request.Url.Segments[2];
            }
            catch (Exception)
            {

            }



            return HttpUtility.UrlDecode(_retValue.Trim('/'));

        }


        /// <summary>
        /// Internal function for retrieving the NonPathPart since it is not available via the framework (internal property)
        /// </summary>
        /// <param name="httpApplication"></param>
        /// <returns></returns>
        internal static string GetNonPathPart(HttpApplication httpApplication)
        {
            if (httpApplication == null)
                throw new ArgumentNullException("HttpApplication", InternalFunctions.GetResourceString("ArgumentNullException", "HttpApplication"));

            string _completePath = httpApplication.Request.Url.AbsoluteUri;
            string _relativePath = httpApplication.Request.Url.AbsolutePath;

            return _completePath.Substring(0, _completePath.Length - _relativePath.Length);
        }

        /// <summary>
        /// Retrieve the resource string
        /// </summary>
        /// <param name="resourceName"></param>
        /// <returns></returns>
        internal static string GetResourceString(string resourceName)
        {
            string _resourceString = "";

            ResourceManager _resourceManager = new ResourceManager("PetkaWebDav.Server.FrameworkResources", Assembly.GetExecutingAssembly());
            if (_resourceManager.GetResourceSet(m_EnglishCulture, true, false) != null)
                _resourceString = _resourceManager.GetString(resourceName, CultureInfo.CurrentUICulture);

            return _resourceString;
        }

        /// <summary>
        /// Retrieve the resource string
        /// </summary>
        /// <param name="resourceName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        internal static string GetResourceString(string resourceName, params string[] parameters)
        {
            string _resourceString = String.Format(CultureInfo.InvariantCulture, GetResourceString(resourceName), parameters);
            return _resourceString;
        }
    }
}
